package commons.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import commons.cloud.MachineType;

public class MachineParser {
	
	public static void main(String[] args) throws IOException {
		Map<MachineType, Map<Long, Double>> machineUsage = readMachineUsage(args[0]);
		Map<Long, Double> consumption = updateConsumption(machineUsage);
		
		//Small Usage
		FileWriter small = new FileWriter(new File("small.output"));
		Map<Long, Double> map = machineUsage.get(MachineType.M1_SMALL);
		for(Entry<Long, Double> entry : consumption.entrySet()){
			small.write("SMALL\t"+entry.getKey()+"\t"+entry.getValue()+"\n");
		}
		small.close();
		
		//Large usage
		FileWriter large = new FileWriter(new File("large.output"));
		map = machineUsage.get(MachineType.M1_LARGE);
		if(map != null){
			for(Entry<Long, Double> entry : map.entrySet()){
				large.write("LARGE\t"+entry.getKey()+"\t"+entry.getValue()+"\n");
			}
		}
		large.close();
		
		//Xlarge usage
		FileWriter xlarge = new FileWriter(new File("xlarge.output"));
		map = machineUsage.get(MachineType.M1_XLARGE);
		if(map != null){
			for(Entry<Long, Double> entry : map.entrySet()){
				xlarge.write("XLARGE\t"+entry.getKey()+"\t"+entry.getValue()+"\n");
			}
		}
		xlarge.close();
	}
	
	private static Map<Long, Double> updateConsumption(Map<MachineType, Map<Long, Double>> hoursUsedPerType) {
		
		Map<Long, Double> hoursUsed = new HashMap<Long, Double>();
		
		//Creating a map of amount of cores used
		for(MachineType machineType : hoursUsedPerType.keySet()){
			Map<Long, Double> usageData = hoursUsedPerType.get(machineType);
			for(Entry<Long, Double> entry : usageData.entrySet()){
				if(machineType.equals(MachineType.M1_SMALL)){
					Double amountOfHours = hoursUsed.get(entry.getKey());
					if(amountOfHours == null){
						amountOfHours = new Double(0);
					}
					hoursUsed.put(entry.getKey(), amountOfHours + entry.getValue());
				}else if(machineType.equals(MachineType.M1_LARGE)){
					Double amountOfHours = hoursUsed.get(entry.getKey() * 2);
					if(amountOfHours == null){
						amountOfHours = new Double(0);
					}
					hoursUsed.put(entry.getKey() * 2, amountOfHours + entry.getValue());
				}else if(machineType.equals(MachineType.M1_XLARGE)){
					Double amountOfHours = hoursUsed.get(entry.getKey() * 4);
					if(amountOfHours == null){
						amountOfHours = new Double(0);
					}
					hoursUsed.put(entry.getKey() * 4, amountOfHours + entry.getValue());
				}
			}
		}
			
		//Updating cumulative consumption
		ArrayList<Long> numberOfMachinesList = new ArrayList<Long>(hoursUsed.keySet());
		Collections.sort(numberOfMachinesList);
		Collections.reverse(numberOfMachinesList);
		
		Map<Long, Double> newUsageData = new HashMap<Long, Double>();
		newUsageData.put(numberOfMachinesList.get(0), hoursUsed.get(numberOfMachinesList.get(0)));//First value
		
		for(Long numberOfMachinesUsed : numberOfMachinesList){//Iterating from the highest number of machines to the lowest one
			double higherUsed = hoursUsed.get(numberOfMachinesUsed);
			
			for(long i = numberOfMachinesUsed - 1; i > 0; i--){//If 15 machines were used for 10 hours, 10 machines were also used for 10 hours ...
				Double lowerUsed = (newUsageData.get(i) != null) ? newUsageData.get(i) : hoursUsed.get(i);
				
				if(lowerUsed != null){
					newUsageData.put(i, lowerUsed+higherUsed);
				}
			}
		}
		
		return newUsageData;
	}
	
	protected static Map<MachineType, Map<Long, Double>> readMachineUsage(String file) {
		//Input format: Provider: + provider.getName();Reserved:reservedResources;OnDemand:onDemandResources
		
		//Reading machine consumption from file
		Map<MachineType, Map<Long, Double>> usagePerType = new HashMap<MachineType, Map<Long,Double>>();
		
		try {
			BufferedReader reader = new BufferedReader(new FileReader(new File(file)));
			while(reader.ready()){
				String[] split = reader.readLine().split(";");//Splitting by ;
				
				String provider = split[0];
				String reservedResources = split[1].split(":")[1];
				String onDemandResources = split[2].split(":")[1];
				
				String reservedMap = reservedResources.substring(1, reservedResources.length()-1);
				String onDemandMap = onDemandResources.substring(1, onDemandResources.length()-1);
				
				Map<MachineType, Long> currentReservationLine = new HashMap<MachineType, Long>();
				Map<MachineType, Long> currentOnDemandLine = new HashMap<MachineType, Long>();
				
				//Reading reserved resources usage map
				if(!reservedMap.isEmpty()){
					String[] reservations = reservedMap.split(",");
					for(int  i = 0; i < reservations.length; i++){
						String[] data = reservations[i].split("=");
						MachineType currentType = MachineType.valueOf(data[0].trim());
						
						currentReservationLine.put(currentType, Long.valueOf(data[1].trim()));
					}
				}
				
				//Reading on-demand resources usage map
				if(!onDemandMap.isEmpty()){
					String[] consumption = onDemandMap.split(",");
					for(int  i = 0; i < consumption.length; i++){
						String[] data = consumption[i].split("=");
						MachineType currentType = MachineType.valueOf(data[0].trim());
						
						currentOnDemandLine.put(currentType, Long.valueOf(data[1].trim()));
					}
				}
				
				//Adding amounts of machines from same type used in different markets
				MachineType[] machineTypes = MachineType.values();
				for(MachineType machineType : machineTypes){
					Long reservedAmount = currentReservationLine.get(machineType);
					reservedAmount = (reservedAmount != null)?reservedAmount:0l;
					Long onDemandAmount = currentOnDemandLine.get(machineType);
					onDemandAmount = (onDemandAmount != null)?onDemandAmount:0l;
					
					Long amountOfMachinesUsed = reservedAmount + onDemandAmount;
					if(amountOfMachinesUsed > 0){
						Map<Long, Double> typeMap = usagePerType.get(machineType);//Consumption map for a certain machine type
						if(typeMap == null){
							typeMap = new HashMap<Long, Double>();
						}
						Double usage = typeMap.get(amountOfMachinesUsed);
						if(usage == null){
							usage = new Double(0);
						}
						typeMap.put(amountOfMachinesUsed, usage+1);
						usagePerType.put(machineType, typeMap);
					}
				}
			}
			reader.close();
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		
		return usagePerType;
	}

}
